package com.atguigu.gmall.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import com.atguigu.gmall.common.util.JwtUtils;
import org.bouncycastle.asn1.esf.SPuri;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * @Author atguigu-mqx
 * @Description TODO 拦截所有 静态资源{css,js,image}，请求资源{请求URL}
 * @Date 2023/5/22 10:15
 * @Version 1.0
 */
@Component
public class AuthFilter implements GlobalFilter {
    /*
    authUrls:
        url: trade.html,myOrder.html,list.html
     */
    @Value("${authUrls.url}")
    private String authUrls; // trade.html,myOrder.html,list.html
    //  声明一个匹配对象
    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private RedisTemplate redisTemplate;
    /**
     *
     * @param exchange web请求对象
     * @param chain 过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //  获取请求路径： http://localhost/api/product/inner/getSkuInfo/21
        //  获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //  获取响应对象
        ServerHttpResponse response = exchange.getResponse();
        //  获取域名后面的url地址 http://list.gmall.com/js/request.js
        String path = request.getURI().getPath(); // js/request.js  api/product/inner/getSkuInfo/21
        //  request.getURI() = http://localhost/api/product/inner/getSkuInfo/21

        //  过滤静态请求资源
        if (antPathMatcher.match("/**/css/**",path) ||
                antPathMatcher.match("/**/js/**",path)||
                antPathMatcher.match("/**/img/**",path)){
            //  放行
            return chain.filter(exchange);
        }
        //  限制用户访问内部数据接口：
        if (antPathMatcher.match("/**/inner/**",path)){
            //  给用户提示信息，没有权限访问.
            return out(response, ResultCodeEnum.PERMISSION);
        }

        //  先获取到用户Id
        String userId = this.getUserId(request);
        //  获取临时用户Id
        String userTempId = this.userTempId(request);

        if ("-1".equals(userId)){
            return out(response,ResultCodeEnum.PERMISSION);
        }
        //  判断
        if (antPathMatcher.match("/**/auth/**",path)){
            //  判断用户是否登录
            if (StringUtils.isEmpty(userId)){
                //  提示未登录
                return out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }

        //  判断用户访问哪些web应用时，必须登录. 我们将需要登录的web应用控制器，写在配置文件中.
        //  判断 path 中是否有以上web应用控制器，如果有并且用户未登录，则拦截还需要跳转到登录页面.
        //  trade.html,myOrder.html,list.html
        //  用户访问的url 是 path
        //  需要分割url
        String[] split = authUrls.split(",");
        if (split!=null && split.length>0){
            //  循环遍历
            for (String url : split) {
                //  if (antPathMatcher.isPattern())
                //  请求的url中包含web应用控制器, 并且用户是未登录状态， 则需要拦截跳转到登录页面.
                if (path.indexOf(url)!=-1 && StringUtils.isEmpty(userId)){
                    //  需要做一个跳转  设置响应状态.
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    //  指定重定向的url： http://passport.gmall.com/login.html?originUrl=http://www.gmall.com/
                    response.getHeaders().set(HttpHeaders.LOCATION,"http://passport.gmall.com/login.html?originUrl="+request.getURI());
                    //  重定向
                    return response.setComplete();
                }
            }
        }

        //  需要将用户Id 放入请求头中
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)){
            //  设置登录用户Id
            if (!StringUtils.isEmpty(userId)){
                //  添加请求头 ServerHttpRequest request
                request.mutate().header("userId", userId).build();
            }
            //  设置未登录用户Id
            if (!StringUtils.isEmpty(userTempId)){
                //  添加请求头 ServerHttpRequest request
                request.mutate().header("userTempId", userTempId).build();
            }
            //  将request --> exchange
            return chain.filter(exchange.mutate().request(request).build());
        }
        //  默认返回方式，放行
        return chain.filter(exchange);
    }

    /**
     * 获取临时用户Id
     * @param request
     * @return
     */
    private String userTempId(ServerHttpRequest request) {
        //  用户Id 存储在缓存中的，要想获取缓存的数据必须先获取到token
        String userTempId = "";
        //  token 可能存储在header或cookie中
        HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
        if (null != httpCookie){
            //  获取cookie中的token 数据
            userTempId = httpCookie.getValue();
            // token = getUsernameFromToken(httpCookie.getValue())
        } else {
            //  有可能从请求头中获取数据  get 请求方式时，能看到请求头携带的token 数据. post 没有携带.
            List<String> list = request.getHeaders().get("userTempId");
            if (!CollectionUtils.isEmpty(list)){
                //  获取cookie中的token 数据
                userTempId = list.get(0);
                //  token = getUsernameFromToken(httpCookie.getValue())
            }
        }
        //  返回临时用户Id
        return userTempId;
    }

    /**
     * 获取用户Id
     * @param request
     * @return
     */
    private String getUserId(ServerHttpRequest request) {
        //  用户Id 存储在缓存中的，要想获取缓存的数据必须先获取到token
        String token = "";
        //  token 可能存储在header或cookie中
        HttpCookie httpCookie = request.getCookies().getFirst("token");
        if (null != httpCookie){
            //  获取cookie中的token 数据
              token = httpCookie.getValue();
//            if (!StringUtils.isEmpty(httpCookie.getValue())){
//                token = JwtUtils.getUUIDFromToken(httpCookie.getValue());
//            }
        } else {
            //  有可能从请求头中获取数据  get 请求方式时，能看到请求头携带的token 数据. post 没有携带.
            List<String> list = request.getHeaders().get("token");
            if (!CollectionUtils.isEmpty(list)){
                //  获取cookie中的token 数据
                  token = list.get(0);
                //  token = JwtUtils.getUUIDFromToken(list.get(0));
            }
        }
        //  判断
        if (!StringUtils.isEmpty(token)){
            //  组成缓存的key
            String loginKey = "user:login:"+token;
            //  从缓存中获取数据
            String userJson = (String) this.redisTemplate.opsForValue().get(loginKey);
            //  判断
            if (!StringUtils.isEmpty(userJson)){
                //  进行数据类型转换
                JSONObject user = JSONObject.parseObject(userJson);
                String ip = (String) user.get("ip");
                //  如果ip地址相同
                if (IpUtil.getGatwayIpAddress(request).equals(ip)){
                    //  获取用户Id
                    String userId = (String) user.get("userId");
                    return userId;
                } else {
                    //  非法登录.
                  return "-1";
                }
            }
        }
        return "";
    }

    /**
     * 用户信息提示
     * @param response
     * @param resultCodeEnum
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        //  响应的内容是：
        //  String message = resultCodeEnum.getMessage();
        //  想输出result 对象
        Result<Object> result = Result.build(null, resultCodeEnum);
        //  将这个对象转换为字符串.
        String content = JSONObject.toJSONString(result);
        //  获取数据流
        DataBuffer wrap = response.bufferFactory().wrap(content.getBytes());
        //  显示内容: 设置文本格式
        response.getHeaders().add("Content-Type","application/json;charset=utf-8");
        //  输出数据
        return response.writeWith(Mono.just(wrap));
    }
}
